Quasi - Static Scoping : Sharing Variable Bindings Across Multiple

نویسنده

  • Daniel P. Friedman
چکیده

Syntax: x; y; z : Var (Internal Names or Variables) Q : Nam (External Names) e : Exp (Expressions) e ::= x j (set! x e) j (e e) j (resolve1 x Q e) j (qs-lambda ((y1 z1) (ym zm)) ((x1 Q1) (xk Qk)) (x) e) Semantic Domains: l : Loc (Locations) : Env = Var ! Loc (Environments) : Inh = Var ! Var (Inheritors) ! : Lnk = Var ! Nam (Links) : Sto = Loc ! Val (Stores) c : Cod = Val ! Env ! Sto ! (Val Sto) (Codes) Proc = Cod Lnk Env (Procedures) v : Val = Proc + (Values) Evaluation Function [[ ]] : Exp ! Lnk ! Env ! Sto ! (Val Sto) [[x]]! = h( ( x)); i [[(set! x e)]]! = let hv; 0i = [[e]]! in hv; ( 0[v=( x)])i [[(ep ea)]]! = let hhc; !u; ri; 0i = [[ep]]! in let hv; 00i = [[ea]]! 0 in c v r 00 [[(resolve1 x Q e)]]! = let hhc; !u; ri; 0i = [[e]]! in hhc; !unnfQg; f( x)=Qg !u ri; 0i [[(lambda (x) e)]]! = let !d = !nfxg, f = nfxg in hh( v 0r 0 : [[e]]!d (( 0r f )[l=x]) ( 0[v=l])); ;; ;i; i where l 62 Dom ( 0) [[(qs-lambda0 ((x1 Q1) (xk Qk)) (x) e)]]! = let !u = fQ1=x1; : : : ;Qk=xkg in let !d = !nfxg[!u], f = nDom (!u)nfxg in hh( v 0r 0 : [[e]]!d (( 0r f)[l=x]) ( 0[v=l])); !u; ;i; i where l 62 Dom ( 0) [[(qs-lambda ((y1 z1) (ym zm)) ((x1 Q1) (xk Qk)) (x) e)]]! = let !o = fQ1=x1; : : : ;Qk=xkg, = fz1=y1; : : : ; zm=ymg in let ! = ! , r = in let !u = ! nDom ( r) !o, !d = !nfxg[! !o], f = nDom ( )nDom (!o)nfxg in hh( v 0r 0 : [[e]]!d (( 0r f )[l=x]) ( 0[v=l])); !u; ri; i where l 62 Dom ( 0) Figure 13: Denotational semantics of Scheme with quasi-static scoping. 10 new procedure's link component is !unnfQg = fQ0=x 2 !u j Q0 6 Qg. Thus, in the resulting triple, the domains of the link and the environment remain disjoint. The code c, however, is carried over to the new procedure unchanged. Hence, the two procedures share the same piece of code but operate on di erent sets of bindings for the quasi-static formal parameters. 4.2.2 Deriving Qs-lambda We develop the meaning of a qs-lambda expression in three stages. First we describe lambda, which is qs-lambda without quasi-static formal parameters. In the second stage we generalize lambda to qs-lambda0, which has quasistatic formal parameters but no lexical inheritance of quasistatic variables. Finally we include quasi-static variable inheritance to get qs-lambda. See Figure 13 in which, for explication purposes, we also include the clauses for lambda and qs-lambda0. The meaning of a lambda expression [[(lambda (x) e)]]! is de ned as follows: let !d = !nfxg, f = nfxg in hh( v 0r 0 : [[e]]!d (( 0r f )[l=x]) ( 0[v=l])); ;; ;i; i where l 62 Dom ( 0) Since there are no quasi-static formals, the resulting triple's link and environment components are both arid. The quasistatic variables declared in the enclosing context of the body expression e are !d = !nfxg. They are the ones declared in the enclosing context of the lambda expression that are not shadowed by the static formal parameter x. The resolved free variables, both static and quasi-static, visible to the body expression e prior to the evaluation of the lambda expression are de ned by the environment f = nfxg. It is the procedure's evaluation time environment but with the static formal parameter x omitted. The resolved free variables visible to e during its evaluation are the variables de ned in f , the resolved quasi-static formal parameters of 0r, and a fresh binding of the static formal parameter x: ( 0r f )[l=x]. But when the code is invoked, the parameter 0r is certain to be associated with ;, since a lambda expression has no quasi-static formals. Also, by the de nition of nite function extension, ( nfxg)[l=x]) is equivalent to [l=x]. Therefore, the triple's code is equivalent to ( v 0r 0 : [[e]] (!nfxg) ( [l=x]) ( 0[v=l])) which, except for the extra link !nfxg and the ignored parameter 0r, is what we have for a procedure in Scheme. Next we de ne the meaning of a qs-lambda0 expression [[(qs-lambda0 ((x1 Q1) (xk Qk)) (x) e)]]! as follows: let !u = fQ1=x1; : : : ;Qk=xkg in let !d = !nfxg[!u], f = nDom (!u)nfxg in hh( v 0r 0 : [[e]]!d (( 0r f )[l=x]) ( 0[v=l])); !u; ;i; i where l 62 Dom ( 0) The resulting triple's link component !u speci es the unresolved quasi-static formals x1; : : : ; xk. Since there is no lexical inheritance, the triple's environment component is arid. The code component is the same as that of a lambda expression, except that !d and f take on di erent meanings. The link !d = !nfxg[!u] captures the quasi-static variables declared with respect to the body expression e. It consists of the most recently speci ed ones and the ones declared in the enclosing context of the qs-lambda0 expression that are not shadowed by the static formal parameter. The resolved free variables available to the body expression when the qs-lambda0 expression is evaluated are de ned by the environment f = nDom (!u)nfxg. They are the ones resolved before the evaluation of the qs-lambda0 expression that are not shadowed by the newly speci ed quasi-static variables or the static formal parameter. In order to verify that lambda is a degenerate case of qs-lambda0, we need only replace !u by ;. Then, let be ((y1 z1) (ym zm)) and be ((x1 Q1) (xk Qk)), we de ne the meaning of a qs-lambda expression [[(qs-lambda (x) e)]]! as follows: let !o = fQ1=x1; : : : ;Qk=xkg, = fz1=y1; : : : ; zm=ymg in let ! = ! , r = in let !u = ! nDom ( r) !o, !d = !nfxg[! !o], f = nDom ( )nDom (!o)nfxg in hh( v 0r 0 : [[e]]!d (( 0r f )[l=x]) ( 0[v=l])); !u; ri; i where l 62 Dom ( 0) This yields a quasi-static procedure hc; !u; ri whose components are de ned in detail below. The syntax denotes the inheritor nite function = fz1=y1; : : : ; zm=ymg and the syntax denotes the link !o = fQ1=x1; : : : ;Qk=xkg. Not shown in the semantic clause is that the inherited quasi-static variables z1; : : : ; zm must be declared in the enclosing context. That is, it is a syntax error unless Rng ( ) Dom (!). The inheriting quasi-static formals y1; : : : ; ym have the same external names as z1; : : : ; zm, respectively. Hence, their link is ! = ! = fQ=y j z=y 2 ;Q=z 2 !g. The inherited quasi-static variables that have been resolved prior to the qs-lambda expression's evaluation are de ned in . So, the environment of the resolved inheriting quasi-static formals is r = = fl=y j z=y 2 11 ; l=z 2 g, which is the third component of the resulting triple. Consequently, the link of the unresolved inheriting quasi-static formals is ! nDom ( r). The link component !u of the procedure's unresolved quasi-static formal parameters is therefore !o ! nDom ( r), the sum of !o and ! nDom ( ). The procedure's code component is the same as that of qs-lambda0, except for the di erent interpretations of !d and f . The link !d of the declared quasi-static variables that are visible to e is !nfxg[! !o]. They are the variables speci ed by the procedure and the variables declared in the enclosing context that are not shadowed by the static formal parameter. The parameter 0r is the procedure's resolved quasi-static formals r when the code is invoked. The environment f of the resolved free variables known to the body expression prior to the qs-lambda expression's evaluation is nDom ( )nDom (!o)nfxg. It is the variables dened in that are not shadowed by the inheriting quasistatic formals y1; : : : ; ym, the quasi-static formals x1; : : : ; xk, or the static formal parameter x. Again, by substituting ; for , it is straightforward to verify that qs-lambda0 is a degenerate case of qs-lambda. 5 Implementation We describe an implementation of quasi-static scoping. It includes the representation of quasi-static procedures, the run-time support for procedure invocations and quasi-static variable references, and the quasi-static variable resolution operation. Since we have designed quasi-static scoping to be as orthogonal to Scheme as possible, we have also made its implementation as independent of Scheme implementations as possible. We represent a quasi-static closure (procedure) (qs-lambda ((iq1 oq1) : : : (iqm oqm)) ((q1 Q1) : : : (qk Qk)) (1) formals body) by a frame of 1+m+k+h consecutive memory slots arranged as follows: hc; s1; : : : ; sm; sm+1; : : : ; sm+k; f1; : : : ; fhi (2) The rst slot c is the location of a static closure representing the ordinary Scheme procedure (lambda formals body). Each of the next m + k slots contains either a location or an external name. The rst m slots are for the inheriting quasi-static formals iq1, : : :, iqm . The next k slots are for the local quasi-static formals q1 , : : :, qk . The last h slots f1; : : : ; fh are locations of the enclosing frames. They are the display [6] of the procedure's free quasi-static variable references. We discuss how the slots are lled later. The values of m and k are readily available from the syntax of the qs-lambda expression itself. The value of h depends on the syntactic context of the qs-lambda expression. It is the number of enclosing qs-lambda expressions. Hence, each frame's shape can be determined statically. Every static closure contains a pointer to a frame through which the procedure's body gains access to the quasi-static variables visible to it. For a static closure created by a lambda expression, this frame is the most recently activated one when the closure is built. For a static closure constructed by a qs-lambda expression, it is the newly created frame. That is, a frame's static closure points back circularly to the frame itself. This circularity is justi able since, when a frame is invoked, the accessible quasi-static variables are de ned in the frame. The additional run-time support then consists of a single register called the Frame Pointer register (FP) that points to the most recently activated frame. Context switching is simple. The activation record of each procedure invocation maintains the caller's FP value. Returning from a procedure invocation simply reinstalls FP to that value. Invoking a procedure, static or quasi-static, requires only saving the current value of FP in the newly created activation record before adjusting it to point to the callee's frame. In case the call is in tail position, however, the callee's activation record assumes the same FP value as that of the caller's. We next explain how a quasi-static variable reference is achieved. We assign to each quasi-static variable reference occurrence a pair of statically determined numbers (d; i) called the lexical address. The number d 0 is the depth between a reference occurrence and its binding occurrence, i.e., the number of qs-lambda expressions between the two occurrences. The other number i is the binding occurrence's slot index in its frame. Using the notation [l] to denote the contents of the location l, the value of a zerodepth reference occurrence, (0; i), is denoted by [[[FP] + i]]. The current frame's location is denoted by [FP]. Adding the index i to it, [FP] + i, gives us the location of the quasistatic variable's slot. Dereferencing it, [[FP] + i], yields the contents of the quasi-static variable's slot. If it contains a location, dereferencing it, [[[FP] + i]], returns the quasi-static variable's value. Otherwise, since an external name is not a location, an attempt to dereference it raises an unbound variable error. The value of a reference occurrence with a lexical address of (d; i), where d 1, requires one more level of indirection. It is denoted by [[[[FP] + t] + i]] where the statically determined number t is d+m+k, with m+k being the number of quasi-static formals in the current frame. Hence [[FP] + t] denotes the frame in which the binding occurrence's slot resides. Besides this extra indirection, the rest is the same as a zero-depth reference. The two kinds of quasi-static variable references described above both refer to the variable's r-value. The l-values, which are needed later, can be obtained as in the r-value cases except for the last dereferencing. That is, [[FP] + i] if 12 the reference occurrence's depth is zero and [[[FP] + t] + i]otherwise.There are two ways to create a new quasi-static frame.First is the evaluation of a qs-lambda expression of (1) thatyields a frame of (2). The location c of the static closure llsthe rst slot. The next m slots are lled with the l-valuesof the inherited quasi-static variables oq1, : : :, oqm, becauseiqi inherits the location or external name (not value) of oqi.Since q1, : : :, qk are unresolved, their slots are lled with theexternal names Q1, : : :, Qk, respectively. That is, sm+i = Qifor i = 1; : : : ; k. The display's frame pointers f1; : : : ; fhget their values from the current frame pointed to by FP.The frame pointer f1 points to the closest frame when thenew frame is invoked (i.e., [FP]). The other frame pointersf2; : : : ; fh are the display of the current frame. Since thecurrent frame's shape is known at compilation, its slot in-dices are known. Therefore it is straightforward to generatethe code needed to copy the display.We can apply two compile-time optimizations at thisstage. First, the frame need not allocate a slot for any ofthe quasi-static formals iq1, : : :, iqm or q1, : : :, qk that is notreferenced in the body. Second, the display need only in-clude the frames that are actually needed in the body. Thatis, if none of the quasi-static variables of an outer frame isreferenced in the body, it can be excluded from the display.The other way to create a new frame is by evaluating aresolve expression(resolve ((x1 Q1) : : : (xn Qn)) qs-proc)First we check that the variables x1, : : :, xn are resolved, i.e.,associated with locations. For each xi that is a quasi-staticvariable, we must make sure that its l-value is a location,not an external name. Furthermore, the expression qs-procmust evaluate to a framehc; s1; : : : ; sm+k ; f1; : : : ; fhiLet li be the location associated with the variable xi .Then the resolve expression returns a new framehc;s01; : : : ;s0m+k ; f1; : : : ; fhiwhere for i = 1; : : : ;m+ k,s0i = lj if si Qj for some n j 1si otherwise:That is, each slot si is compared against the external namesQ1, : : :, Qn. If there is a match with Qj, the correspondingslots0i is the location lj . Otherwise, si is either a locationor a di erent external name. Hence,s0i is the same as si.We should point out that without su cient data ow in-formation, each lexical variable xi must be assumed to beassignable. As a result, optimizations performed by manyScheme compilers on a lexical variable that is free of side-e ects are not applicable to xi.Finally we sketch a simple variation of the implemen-tation presented above. The purpose of a frame's displayis to gain access to the slots of the procedure's free quasi-static variables. Hence, instead of copying a sequence offrame pointers, we could copy down the slots of the freequasi-static variables. The result is reminiscent of the wayCardelli's Functional Abstract Machine [3] handles a pro-cedure's free lexical variable references. It would requirepotentially more time and space to build a frame. On theother hand, it reduces every quasi-static variable reference'slexical address to a depth of zero. Hence, it speeds up freequasi-static variable referencings.6 Comparisons and Future WorkWe accomplish variable sharing across lexical scopes with re-solvers that selectively \export" variable bindings and quasi-static procedures that selectively \import" variable bind-ings. Resolvers are run-time linking operators and quasi-static procedures are compiled but only partially linked pro-grams.MIT Scheme's rst-class environments [1, 12] resembleour resolvers. They di er from our quasi-static scoping infour aspects. First, there is no mechanism like resolvethat can export variables selectively. Consequently, in thepresence of rst-class environments, optimization techniquessuch as constant folding that involve eliminating variablesare no longer meaning preserving transformations. Second,rst-class environments rely on the internal names of vari-ables. Hence, -conversion is invalid on some variables. Thisimposes a serious problem on macro systems since generatedvariable names could cause inadvertent capturing. Third,eval, the user-accessible interpreter, compiles source code atrun time and the same piece of source code is compiled asmany times as it is used. Thus, the performance is inferior toour approach in which each piece of source is compiled onlyonce. Fourth, environment is a notion that is not employedby every computational model. Thus, adding rst-class en-vironments to a language means that the language cannot beimplemented easily on some environment-less architecturessuch as the G-machine [16]. Jagannathan's environment-based re ection language Rascal [9] is another language withrst-class environments. Rascal provides a way to identifyrei able (exportable) variables; however, it still su ers fromthe rst two disadvantages mentioned above, because somenon-rei able variables are dynamically bound.Lamping's uni ed system of parameterization [10] em-phasizes the other part of our approach. It uses a specialform data to identify non-lexical variables. But instead ofproviding an explicit resolution operation like resolve , italways uses the run-time dynamic environment to resolvethe non-lexical variables. They are therefore truly dynamicvariables. Consequently, programs are harder to analyze andthe language is di cult to implement e ciently.13 Quasi-static scoping can be characterized as a mecha-nism for run-time linking as opposed to the compile-timelinking provided by other systems. It is just one point in thespectrum of linking disciplines ranging from static (compile-time) to dynamic (run-time). We are convinced that ina programming environment various linking disciplines arenecessary for di erent purposes. Our goal is therefore toinvestigate if there exists a small set of linking disciplinesthat will su ce for all needs. If not, we would like to es-tablish a metric for measuring the degree of \staticity," orequivalently \dynamicity," in order to classify all the possi-ble disciplines.We are currently exploring various ways of speeding upthe resolution process. For instance, we can assign types toboth resolvers and quasi-static procedures and initiate theresolution process only if their types match. Then, run-timeexternal name matching would be eliminated.Finally, for reasoning about quasi-static scoping, we havedeveloped a calculus that is consistent and has a standard-ization procedure. The calculus is an extension of Felleisenand Hieb's calculus of sequential state [8], which in turn isan extension of Plotkin's call-by-value -calculus [17]. Wewill continue its development into a more complete logicalsystem and report it elsewhere.Acknowledgements. We are grateful for the insightfulcomments by Kent Dybvig, Chris Haynes, and the late BobHieb during the early stages of this research. We would alsolike to thank Mike Ashley, Matthias Felleisen, Julia Lawall,Jon Rossie, and John Simmons for their comments on earlierdrafts of this paper.References[1] H. Abelson and G. J. Sussman with J. Sussman. Struc-ture and Interpretation of Computer Programs. MITPress, 1985.[2] N. Adams and J. Rees. Object-oriented programming inScheme. In Proceedings of the ACM Conference on Lispand Functional Programming, pages 277{288, 1988.[3] L. Cardelli. Compiling a functional language. In Pro-ceedings of the ACM Conference on Lisp and FunctionalProgramming, pages 208{217, 1984.[4] W. Clinger and J. Rees (editors). Revised4 reporton the algorithmic language Scheme. Lisp Pointers,4(3):1{55, 1991.[5] P. Curtis and J. Rauen. A module system for Scheme.In Proceedings of the ACM Conference on Lisp andFunctional Programming, pages 13{19, 1990.[6] E. W. Dijkstra. Algol 60 translation. Supplement AL-GOL Bulletin 10, 1960.[7] M. Felleisen and D. P. Friedman. A closer look at ex-port and import statements. Journal of Computer Lan-guages, 11(1):29{37, 1986.[8] M. Felleisen and R. Hieb. The revised report on thesyntactic theories of sequential control and state. The-oretical Computer Science, 102:235{271, 1992.[9] S. Jagannathan. Re ective building blocks for modu-lar systems. To appear in the IMSA '92 InternationalWorkshop on Re ection and Meta-Level Architecture.[10] J. O. Lamping. A uni ed system of parameteriza-tion for programming languages. In Proceedings of theACM Conference on Lisp and Functional Programming,pages 316{326, 1988.[11] K. J. Lang and B. A. Pearlmutter. Oaklisp: An object-oriented dialect of Scheme. Lisp and Symbolic Compu-tation, 1(1):39{51, 1988.[12] J. S. Miller and G. J. Rozas. Free variables and rst-class environments. Lisp and Symbolic Computation,4(2):107{141, 1991.[13] R. Milner, M. Tofte, and R. Harper. The De nition ofStandard ML. MIT Press, 1990.[14] J. H. Morris Jr. Protection in programming languages.CACM, 16(8):15{21, 1973.[15] K. N rmark. Simulation of object-oriented conceptsand mechanisms in Scheme. Technical Report R 90-01, Institute of Electronic Systems, Aalborg University,January 1990.[16] S. L. Peyton Jones. The Implementation of FunctionalProgramming Languages. Prentice Hall, 1987.[17] G. D. Plotkin. Call-by-name, call-by-value and the-calculus. Theoretical Computer Science, 1:125{159,1975.[18] G. L. Steele Jr. Common Lisp: The Language. DigitalPress, second edition, 1990.14

برای دانلود متن کامل این مقاله و بیش از 32 میلیون مقاله دیگر ابتدا ثبت نام کنید

ثبت نام

اگر عضو سایت هستید لطفا وارد حساب کاربری خود شوید

منابع مشابه

Scoping Constructs for Program Generators

Program generation is the process of generating code in a high-level language (e.g., C, C++, Java) to implement an abstract specification of a program. Generated programs are created by synthesizing and composing code fragments. Binding identifiers in generated code with their correct variable declarations has been the focus of a lot of research work in the context of macro-expansion (e.g., hyg...

متن کامل

Quasi-static antenna array processing for rapidly time-varying channels

This paper explores the use of quasi-static frequency-domain antenna combining weights for multi-user (e.g., SDMA) or multi-stream (e.g., MIMO) communication systems operating in rapidly time-varying frequency-selective channels. By implementing combining weights that are constant across a time slot but are updated from slot-to-slot (i.e., “quasi-static”), great computational complexity savings...

متن کامل

Scoping Constructs for Software Generators

A well-known problem in program generation is scoping. When identifiers (i.e., symbolic names) are used to refer to variables, types, or functions, program generators must ensure that generated identifiers are bound to their intended declarations. This is the standard scoping issue in programming languages, only automatically generated programs can quickly become too complex and maintaining bin...

متن کامل

Stimulus–response bindings code both abstract and specific representations of stimuli: evidence from a classification priming design that reverses multiple levels of response representation

Repetition priming can be caused by the rapid retrieval of previously encoded stimulus-response (S-R) bindings. S-R bindings have recently been shown to simultaneously code multiple levels of response representation, from specific Motor-actions to more abstract Decisions ("yes"/"no") and Classifications (e.g., "man-made"/"natural"). Using an experimental design that reverses responses at all of...

متن کامل

Quasi-Static Scheduling for SCORE

Previous work introduced a dynamic compute model aimed at eliminating existing barriers to the widespread efficient exploitation of reconfigurable devices. Among other achievements this model decoupled application design-time decisions from the run-time physical resource bindings. The compute model uses graphs of compute pages and memory blocks connected by stream links to capture the definitio...

متن کامل

ذخیره در منابع من


  با ذخیره ی این منبع در منابع من، دسترسی به آن را برای استفاده های بعدی آسان تر کنید

برای دانلود متن کامل این مقاله و بیش از 32 میلیون مقاله دیگر ابتدا ثبت نام کنید

ثبت نام

اگر عضو سایت هستید لطفا وارد حساب کاربری خود شوید

عنوان ژورنال:

دوره   شماره 

صفحات  -

تاریخ انتشار 1993